/*
 * Copyright (C) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef AVCODEC_VIDEO_ENCODER_DEMO_H
#define AVCODEC_VIDEO_ENCODER_DEMO_H
#include <fstream>
#include <atomic>
#include <multimedia/av_codec/native_avmemory.h>
#include <queue>
#include <string>
#include <thread>
#include <map>
#include <cstddef>
#include <cstdio>

#include <multimedia/av_codec/native_avcodec_base.h>
class VEncSignal {
public:
    std::mutex inMutex_;
    std::mutex outMutex_;
    std::condition_variable inCond_;
    std::condition_variable outCond_;
    std::queue<uint32_t> inQueue_;
    std::queue<uint32_t> outQueue_;
    std::queue<uint32_t> dataQueue_;
    std::queue<OH_AVMemory *> inBufferQueue_;
    std::queue<OH_AVMemory *> outBufferQueue_;
    std::queue<OH_AVMemory *> dataBufferQueue_;
    std::queue<OH_AVCodecBufferAttr> outAttrQueue_;
    std::queue<OH_AVCodecBufferAttr> inAttrQueue_;
};
class AVCodecCallback {
public:
    virtual ~AVCodecCallback() = default;
    virtual void OnCodecError(int32_t errorCode) = 0;
    virtual void OnOutputFormatChanged(OH_AVFormat* format) = 0;
    virtual void OnInputBufferAvailable(uint32_t index, OH_AVMemory* data) = 0;
    virtual void OnOutputBufferAvailable(uint32_t index, OH_AVMemory* data, OH_AVCodecBufferAttr* attr) = 0;
};

class VEncDemo :AVCodecCallback{
public:
    VEncDemo();
    virtual ~VEncDemo();
    void RunCase();
    void StopCase();
    void OnCodecError(int32_t errorCode) override;
    void sendDataPackage(unsigned char *data,int32_t len,int64_t timestamp);
    void OnOutputFormatChanged(OH_AVFormat* format) override;
    void OnInputBufferAvailable(uint32_t index, OH_AVMemory* data) override;
    void OnOutputBufferAvailable(uint32_t index, OH_AVMemory* data, OH_AVCodecBufferAttr* attr) override;


private:
    int32_t CreateEnc();
    int32_t Configure(OH_AVFormat *format);
    int32_t GetSurface(OHNativeWindow *window);
    int32_t Start();
    int32_t Stop();
    int32_t Flush();
    int32_t Reset();
    int32_t Release();
    int32_t ExtractPacket();
    void InputFunc();
    void OutputFunc(uint32_t index,OH_AVMemory* data,OH_AVCodecBufferAttr* attr);
    static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData);
    static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData);
    static void OnNeedInputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData);
    static void OnNewOutputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data,
            OH_AVCodecBufferAttr *attr, void *userData);
    static void SetCallback(OH_AVCodec *codec, AVCodecCallback* cb);
    static void DelCallback(OH_AVCodec *codec);
    static AVCodecCallback* GetCallback(OH_AVCodec *codec);
    
    static std::mutex mutex_;
    static std::map<OH_AVCodec *, AVCodecCallback* > callBackMap_;
    OH_AVCodec *videoEnc_ = nullptr;

    std::atomic<bool> isRunning_;
    std::unique_ptr<std::ofstream> outFile_;
    std::unique_ptr<std::ifstream> inputFile_;
    std::unique_ptr<std::thread> inputLoop_;
    std::unique_ptr<std::thread> outputLoop_;
    VEncSignal *signal_;
    bool isFirstFrame_ = false;
    int64_t timeStamp_ = 0;

    // Extract packet
    static constexpr int32_t VIDEO_INBUF_SIZE = 640*480*1.5;
    static constexpr int32_t VIDEO_REFILL_THRESH = 4096;
    size_t data_size_ = 0;
    uint8_t *data_ = nullptr;
    uint8_t inbuf_[VIDEO_INBUF_SIZE];
    bool file_end_ = false;
    FILE* fout;
};
#endif // AVCODEC_VIDEO_ENCODER_DEMO_H
